#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h> // mmap munmap
#include <sys/wait.h> // waitpid wait
#include <string.h>

int main(int argc, char const *argv[]) {
    // step 1# 构造共享内存对象名称，使用当前进程 PID 保证唯一
    char shmName[100] = {0};
    sprintf(shmName, "/share%d", getpid());

    // step 2# 创建共享内存对象，返回文件描述符
    int fd = shm_open(shmName, O_CREAT | O_RDWR, 0644); // O_CREAT：若不存在则创建；O_RDWR：可读写
    if (fd < 0) {
        perror("共享内存对象开启失败!\n");
        exit(EXIT_FAILURE);
    }

    // step 3# 设置共享内存大小为 100 字节
    ftruncate(fd, 100);

    // step 4# 将共享内存对象映射到当前进程地址空间
    char *share = mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (share == MAP_FAILED) {
        perror("共享内存对象映射到内存失败!\n");
        exit(EXIT_FAILURE);
    }

    // step 5# 映射完成后可关闭文件描述符，释放资源（不影响映射）
    close(fd);

    // step 6# 创建子进程
    pid_t pid = fork();
    if (pid < 0) {
        perror("创建子进程失败!\n");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // step 7# 子进程向共享内存写入数据
        strcpy(share, "子进程到此一游");
        printf("子进程%d完成回复: %s\n", getpid(), share);
    } else {
        // step 8# 父进程等待子进程写入后读取共享内存内容
        sleep(1); // 确保子进程先写入
        printf("父进程%d收到子进程%d回复的内容：%s\n", getpid(), pid, share);

        // step 9# 等待子进程结束
        wait(NULL);

        // step 10# 释放映射区域
        int ret = munmap(share, 100);
        if (ret == -1) {
            perror("munmap");
            exit(EXIT_FAILURE);
        }
    }

    // step 11# 删除共享内存对象，避免资源泄漏
    shm_unlink(shmName);

    return 0;
}
